home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 06 - 1990 / 06.09 Sep 90 / Canon Sources / New Canon / Canon.c next >
Encoding:
C/C++ Source or Header  |  1990-02-07  |  24.7 KB  |  1,196 lines  |  [TEXT/nX^n]

  1. //////////////////////////////////////////////////////////////////
  2. //    
  3. //        Canon.c
  4. //        -------
  5. //        New MPW Canon tool
  6. //    
  7. //////////////////////////////////////////////////////////////////
  8.     
  9. #pragma load "managers"
  10.     
  11. //////////////////////////////////////////////////////////////////
  12. //    
  13. //        Constants and Macros
  14. //    
  15. //////////////////////////////////////////////////////////////////
  16.     
  17. #define nil                    0
  18.     
  19. #define stdinfd            0
  20. #define stdoutfd            1
  21. #define stderrfd            2
  22.     
  23. #define stdunit(x)        ((x >= stdinfd) && (x <= stderrfd))
  24. #define notstdunit(x)    (x > stderrfd)
  25.  
  26. #define nombuffsize        1024
  27. #define truebuffsize        1200
  28.  
  29. #define classcount        15
  30. #define idstate            7
  31.     
  32. //////////////////////////////////////////////////////////////////
  33. //    
  34. //        Types
  35. //    
  36. //////////////////////////////////////////////////////////////////
  37.     
  38. typedef enum {false, true} logical;
  39. typedef enum {nocode, pascalcode, ccode} codetype;
  40.  
  41. typedef struct node
  42.     {
  43.     char                *key;
  44.     struct node        *left;
  45.     struct node        *right;
  46.     int                balance;
  47.     char                *data;
  48.     } node;
  49.     
  50. //////////////////////////////////////////////////////////////////
  51. //    
  52. //        Globals
  53. //    
  54. //////////////////////////////////////////////////////////////////
  55.     
  56.     unsigned char    *CASETABLE;
  57.     
  58. //////////////////////////////////////////////////////////////////
  59. //    
  60. //        Prototypes
  61. //    
  62. //////////////////////////////////////////////////////////////////
  63.     
  64.     void initmac();
  65.     int openoutput(char *thename, int output);
  66.     int readinput(int input, Handle inbuffer);
  67.     int filter(char *inbuffer, int buffersize, int output,
  68.                     codetype language, node *symbols);
  69.     int writeoutput(int output, char *outbuffer, int buffersize);
  70.     node *parser(char *dictname, codetype language);
  71.     int gettoken(char *buffer, int buffersize, char *thestring,
  72.                     char *classtable, char *statetable);
  73.     node *createnode(char *thekey, char *thedata);
  74.     unsigned int insert(node *parent, char *thekey, char *thedata, int depth);
  75.     node *lookup(node *thetable, char *thekey);
  76.     void destroy(node *thetable);
  77.     void dump(node *thetable);
  78.     int compare(unsigned char *string1, unsigned char *string2);
  79.     
  80. //////////////////////////////////////////////////////////////////
  81. //
  82. //        main
  83. //        ----
  84. //        the "main" routine reads and interprets the command line,
  85. //        reads the dictionary file, and filters the input
  86. //
  87. //////////////////////////////////////////////////////////////////
  88.     
  89. int main(int argc, char *argv[])
  90.     {
  91.     
  92.     int                output;
  93.     logical            sensitive;
  94.     codetype            language;
  95.     char                *outputname;
  96.     char                *dictname;
  97.     logical            errors;
  98.     int                index;
  99.     char                *thetail;
  100.     Handle            thehandle;
  101.     node                *symbols;
  102.     int                input;
  103.     int                buffersize;
  104.     
  105.     initmac();
  106.     
  107. //    "output" is the fd of the output file, initially stdout
  108. //    "sensitive" is the case sensitivity, initially insensitive
  109. //    "language" is the language to parse, initially unknown
  110.     
  111.     output = stdoutfd;
  112.     sensitive = false;
  113.     language = nocode;
  114.     
  115. //    "outputname" is the name of the output file
  116. //    "dictname" is the name of the dictionary file
  117. //    "errors" is the error flag, initially indicating no errors
  118.     
  119.     outputname = nil;
  120.     dictname = nil;
  121.     errors = false;
  122.     
  123. //    command line interpreter: first pass
  124.     
  125.     for (index = 1; index < argc; index++)
  126.         {
  127.         
  128.         if (argv[index][0] == '-')
  129.             {
  130.             
  131.             switch (argv[index][1])
  132.                 {
  133.     
  134. //    "-p" and "-c" options set language type;
  135. //        these override any previous setting
  136.                 
  137.                 case 'C':
  138.                 case 'c':
  139.                     language = ccode;
  140.                     break;
  141.                 
  142.                 case 'P':
  143.                 case 'p':
  144.                     language = pascalcode;
  145.                     break;
  146.     
  147. //    "-s" option make Canon case sensitive
  148.                 
  149.                 case 'S':
  150.                 case 's':
  151.                     sensitive = true;
  152.                     break;
  153.     
  154. //    "-o" option names the output file; remember the name
  155. //        and read the file later
  156.                 
  157.                 case 'O':
  158.                 case 'o':
  159.                     index++;
  160.                     if (outputname == nil)
  161.                         outputname = argv[index];
  162.                     else
  163.                         {
  164.                         fprintf(stderr, "Error - multiple output "
  165.                                         "files %s and %s!\n",
  166.                                         outputname, argv[index]);
  167.                         errors = true;
  168.                         }
  169.                     break;
  170.     
  171. //    "-d" option names the dictionary file; remember the name
  172. //        and read the file later
  173.                 
  174.                 case 'D':
  175.                 case 'd':
  176.                     index++;
  177.                     if (dictname == nil)
  178.                         dictname = argv[index];
  179.                     else
  180.                         {
  181.                         fprintf(stderr, "Error - multiple dictionary "
  182.                                         "files %s and %s!\n",
  183.                                         dictname, argv[index]);
  184.                         errors = true;
  185.                         }
  186.                     break;
  187.                 
  188.                 default:
  189.                     fprintf(stderr, "Error - Unknown option %s\n",
  190.                                     argv[index]);
  191.                     errors = true;
  192.                     break;
  193.                 
  194.                 }
  195.             
  196.             }
  197.         else if (language == nocode)
  198.             {
  199.     
  200. //    argv[index] is the name of an input file; if
  201. //        "language" has not changed since initialization,
  202. //        set "language" according to file name
  203.             
  204.             thetail = argv[index] + strlen(argv[index]) - 2;
  205.             if (strcmp(thetail, ".p") == 0)
  206.                 language = pascalcode;
  207.             else if (strcmp(thetail, ".c") == 0)
  208.                 language = ccode;
  209.             
  210.             }
  211.         
  212.         }
  213.     
  214. //    exit if errors were found in the first pass
  215.     
  216.     if (errors)
  217.         exit(2);
  218.     
  219. //    if "language" is still unknown, set it to Pascal
  220.     
  221.     if (language == nocode)
  222.         language = pascalcode;
  223.     
  224. //    load the case table
  225.     
  226.     if (sensitive)
  227.         thehandle = GetResource('TABL', 4002);
  228.     else
  229.         thehandle = GetResource('TABL', 4001);
  230.     HLock(thehandle);
  231.     CASETABLE = (unsigned char *)*thehandle;
  232.     
  233. //    copy the dictionary into the symbol table
  234.     
  235.     if (dictname == nil)
  236.         {
  237.         fprintf(stdout, "Error - no dictionary file specified!\n");
  238.         exit(2);
  239.         }
  240.     
  241.     symbols = parser(dictname, language);
  242.     if (symbols == nil)
  243.         exit(2);
  244.     
  245. //    if "outputname" has been found, open the output file
  246.     
  247.     if (outputname != nil)
  248.         {
  249.         output = openoutput(argv[++index], output);
  250.         if (output < 0)
  251.             {
  252.             fprintf(stderr, "Error - Unable to open "
  253.                             "output file %s!\n", argv[index]);
  254.             exit(2);
  255.             }
  256.         }
  257.     
  258. //    "input" is the fd of the input file, initially stdin
  259. //    "thehandle" is the input buffer, initially empty
  260. //    "buffersize" is the size of "thehandle"
  261.     
  262.     input = stdinfd;
  263.     thehandle = NewHandle(0);
  264.     buffersize = 0;
  265.     
  266. //    command line interpreter: second pass
  267.     
  268.     for (index = 1; index < argc; index++)
  269.         {
  270.     
  271. //    skip all options (read in first pass)
  272.         
  273.         if (argv[index][0] == '-')
  274.             {
  275.             switch (argv[index][1])
  276.                 {
  277.                 case 'D':
  278.                 case 'O':
  279.                 case 'd':
  280.                 case 'o':
  281.                     index++;
  282.                 }
  283.             }
  284.         else
  285.             {
  286.     
  287. //    argv[index] is the name of an input file; open
  288. //        the file and read it into the input buffer
  289.             
  290.             input = open(argv[index], O_RDONLY);
  291.             if (input < 0)
  292.                 {
  293.                 fprintf(stderr, "Error - Unable to open "
  294.                                 "input file %s!\n", argv[index]);
  295.                 exit(2);
  296.                 }
  297.             
  298.             buffersize = readinput(input, thehandle);
  299.             if (buffersize < 0)
  300.                 {
  301.                 fprintf(stderr, "Error - Reading from %s!\n", argv[index]);
  302.                 exit(2);
  303.                 }
  304.             
  305.             close(input);
  306.             
  307. //    call "filter" to read the input buffer and write
  308. //        filtered output
  309.             
  310.             HLock(thehandle);
  311.             filter(*thehandle, buffersize, output, language, symbols);
  312.             HUnlock(thehandle);
  313.             
  314.             }
  315.         
  316.         }
  317.     
  318. //    if "input" is still a standard unit number, then no
  319. //        input file was opened, and input must be from
  320. //        standard input
  321.     
  322.     if (stdunit(input))
  323.         {
  324.         
  325.         buffersize = readinput(input, thehandle);
  326.         if (buffersize < 0)
  327.             {
  328.             fprintf(stderr, "Error - Reading from standard input!\n");
  329.             exit(2);
  330.             }
  331.         
  332. //    call "filter" to read the input buffer and write
  333. //        filtered output
  334.         
  335.         HLock(thehandle);
  336.         filter(*thehandle, buffersize, output, language, symbols);
  337.         HUnlock(thehandle);
  338.         
  339.         }
  340.     
  341. //    wrapup:  dispose of the input buffer, close "output"
  342. //        if the program opened it and dispose of the symbol table
  343.     
  344.     DisposHandle(thehandle);
  345.     
  346.     if (notstdunit(output))
  347.         close(output);
  348.     destroy(symbols);
  349.     
  350.     exit(0);
  351.     
  352.     }
  353.     
  354. //////////////////////////////////////////////////////////////////
  355. //
  356. //        initmac
  357. //        -------
  358. //        initialize any necessary managers and whatnot.
  359. //
  360. //////////////////////////////////////////////////////////////////
  361.     
  362. void initmac()
  363.     {
  364.     
  365.     InitGraf((Ptr)&qd.thePort);
  366.     SetFScaleDisable(true);
  367.     
  368.     InitCursorCtl(nil);
  369.     
  370.     }
  371.     
  372. //////////////////////////////////////////////////////////////////
  373. //
  374. //        openoutput
  375. //        ----------
  376. //        open the output file.  returns the fd or, if an error
  377. //        occurs, the error flag.
  378. //
  379. //////////////////////////////////////////////////////////////////
  380.     
  381. int openoutput(char *thename, int output)
  382.     {
  383.     
  384.     FInfo                theinfo;
  385.     
  386. //    if "output" is not a standard unit, then an output
  387. //        file must have already be open
  388.     
  389.     if (notstdunit(output))
  390.         {
  391.         fprintf(stderr, "Warning - additional output "
  392.                         "file %s ignored!\n", thename);
  393.         return(output);
  394.         }
  395.  
  396. //    open the output file for writing (O_WRONLY),
  397. //        creating it if necessary (O_CREAT) and
  398. //        zeroing it otherwise (O_TRUNC)
  399.     
  400.     output = open(thename, O_WRONLY + O_CREAT + O_TRUNC);
  401.     if (output < 0)
  402.         return(output);
  403.  
  404. //    if the file was created by "open", it will be
  405. //        untyped, so set the type to TEXT and MPS
  406.     
  407.     if (getfinfo(thename, 0, &theinfo))
  408.         {
  409.         fprintf(stderr, "Warning - unable to get info for "
  410.                         "output file %s!\n", thename);
  411.         return(output);
  412.         }
  413.     
  414.     theinfo.fdType = 'TEXT';
  415.     theinfo.fdCreator = 'MPS ';
  416.     
  417.     if (setfinfo(thename, 0, &theinfo))
  418.         fprintf(stderr, "Warning - unable to set info for "
  419.                         "output file %s!\n", thename);
  420.     
  421.     return(output);
  422.     
  423.     }
  424.     
  425. //////////////////////////////////////////////////////////////////
  426. //
  427. //        readinput
  428. //        ---------
  429. //        this routine reads an input file into the input buffer and
  430. //        returns the new size of the buffer or, if a read error
  431. //        occurs, the error flag.
  432. //
  433. //////////////////////////////////////////////////////////////////
  434.     
  435. int readinput(int input, Handle thehandle)
  436.     {
  437.     
  438.     int                buffersize;
  439.     int                readsize;
  440.     
  441.     buffersize = 0;
  442.     
  443.     SetHandleSize(thehandle, 1024);
  444.     HLock(thehandle);
  445.     
  446.     while ((readsize = read(input, *thehandle + buffersize, 1024)) > 0)
  447.         {
  448.         buffersize += readsize;
  449.         HUnlock(thehandle);
  450.         SetHandleSize(thehandle, buffersize + 1024);
  451.         HLock(thehandle);
  452.         }
  453.     
  454.     if (readsize < 0)
  455.         return(readsize);
  456.     
  457.     HUnlock(thehandle);
  458.     SetHandleSize(thehandle, buffersize + 1024);
  459.     
  460.     return(buffersize);
  461.     
  462.     }
  463.     
  464. //////////////////////////////////////////////////////////////////
  465. //
  466. //        filter
  467. //        ------
  468. //        this routine does the main work of the program.
  469. //
  470. //////////////////////////////////////////////////////////////////
  471.     
  472. int filter(char *inbuffer, int buffersize, int output,
  473.                 codetype language, node *symbols)
  474.     {
  475.     
  476.     int                inposition;
  477.     int                outposition;
  478.     int                thetoken;
  479.     node                *thenode;
  480.     int                thelength;
  481.     Handle            thehandle;
  482.     char                *classtable;
  483.     char                *statetable;
  484.     char                outbuffer[truebuffsize];
  485.     int                thestate;
  486.     unsigned char    thechar;
  487.     int                theclass;
  488.     int                newstate;
  489.     int                writesize;
  490.  
  491. //    “inposition” is the current read position
  492. //    “outposition” is the current write position
  493. //    “thetoken” is the position of the beginning of the
  494. //        current identifier
  495.     
  496.     inposition = 0;
  497.     outposition = 0;
  498.     thetoken = 0;
  499.  
  500. //    “classtable” converts characters into classes
  501.     
  502.     thehandle = GetResource('TABL', 1001);
  503.     HLock(thehandle);
  504.     classtable = *thehandle;
  505.  
  506. //    “statetable” is the state machine
  507.     
  508.     if (language == pascalcode)
  509.         thehandle = GetResource('TABL', 3001);
  510.     else
  511.         thehandle = GetResource('TABL', 3002);
  512.     HLock(thehandle);
  513.     statetable = *thehandle;
  514.     
  515. //    start the machine in state 0
  516.     
  517.     thestate = 0;
  518.     while (inposition < buffersize)
  519.         {
  520.  
  521. //    read the next character, find its class
  522. //        and the new state
  523.         
  524.         thechar = inbuffer[inposition++];
  525.         theclass = classtable[thechar];
  526.         newstate = statetable[classcount * thestate + theclass];
  527.         
  528.         switch (newstate)
  529.             {
  530.  
  531. //    found an identifier:  if it is in the symbol table,
  532. //        replace it with the table's data.  Then go to
  533. //        state 0.
  534.             
  535.             case -1:
  536.                 inposition--;
  537.                 outbuffer[outposition] = '\0';
  538.                 thenode = lookup(symbols, &outbuffer[thetoken]);
  539.                 if (thenode != nil)
  540.                     {
  541.                     outposition -= strlen(&outbuffer[thetoken]);
  542.                     thelength = strlen(thenode->data);
  543.                     BlockMove((Ptr)thenode->data,
  544.                                     &outbuffer[outposition], thelength);
  545.                     outposition += thelength;
  546.                     }
  547.                 newstate = 0;
  548.                 break;
  549.  
  550. //    retract if going from state 2 to state 0; otherwise,
  551. //        copy input to output
  552.             
  553.             case 0:
  554.                 if (thestate == 2)
  555.                     inposition--;
  556.                 else
  557.                     outbuffer[outposition++] = thechar;
  558.                 break;
  559.  
  560. //    reading an identifier:  if this is the beginning,
  561. //        record the position for later use.  Then, fall
  562. //        through to the default
  563.             
  564.             case idstate:
  565.                 if (thestate != idstate)
  566.                     thetoken = outposition;
  567.  
  568. //    all other cases, copy input to output
  569.             
  570.             default:
  571.                 outbuffer[outposition++] = thechar;
  572.                 break;
  573.             
  574.             }
  575.  
  576. //    if the output buffer fills up, and we're not in the
  577. //        middle of an identifier, write it to disk
  578.         
  579.         if ((outposition >= nombuffsize)
  580.                         && (thestate != idstate) && (newstate != idstate))
  581.             {
  582.             outposition = writeoutput(output, outbuffer, outposition);
  583.             if (outposition < 0)
  584.                 return(outposition);
  585.             }
  586.         
  587.         thestate = newstate;
  588.         
  589.         }
  590.  
  591. //    write the output buffer to disk
  592.     
  593.     writesize = write(output, outbuffer, outposition);
  594.     return(writesize);
  595.     
  596.     }
  597.     
  598. //////////////////////////////////////////////////////////////////
  599. //
  600. //        writeoutput
  601. //        ----------
  602. //        this routine flushes the output buffer by writing
  603. //        it to the output file.  It returns the new size of the
  604. //        buffer or, if a write error occurs, the error flag.
  605. //
  606. //////////////////////////////////////////////////////////////////
  607.     
  608. int writeoutput(int output, char *outbuffer, int buffersize)
  609.     {
  610.     
  611.     int                writesize;
  612.     
  613.     writesize = write(output, outbuffer, nombuffsize);
  614.     
  615.     if (writesize < 0)
  616.         return(writesize);
  617.     
  618.     buffersize -= writesize;
  619.     BlockMove(outbuffer + writesize, outbuffer, buffersize);
  620.     
  621.     return(buffersize);
  622.     
  623.     }
  624.     
  625. //////////////////////////////////////////////////////////////////
  626. //
  627. //        parser
  628. //        ------
  629. //        this routine creates the symbol table and stores the
  630. //        Canon dictionary in it.
  631. //
  632. //////////////////////////////////////////////////////////////////
  633.     
  634. node *parser(char *dictname, codetype language)
  635.     {
  636.     
  637.     Handle            thehandle;
  638.     char                *parsetable;
  639.     char                *classtable;
  640.     char                *statetable;
  641.     int                thefile;
  642.     int                buffersize;
  643.     char                *buffer;
  644.     node                *symbols;
  645.     int                thestate;
  646.     int                newstate;
  647.     int                theline;
  648.     int                errors;
  649.     int                thetoken;
  650.     char                thekey[256];
  651.     char                thedata[256];
  652.     char                dummy[256];
  653.     char                *thestring;
  654.     
  655. //    "parsetable" is the parser's state machine
  656.     
  657.     thehandle = GetResource('TABL', 1000);
  658.     HLock(thehandle);
  659.     parsetable = *thehandle;
  660.     
  661. //    "classtable" is the character class table
  662.     
  663.     thehandle = GetResource('TABL', 1001);
  664.     HLock(thehandle);
  665.     classtable = *thehandle;
  666.     
  667. //    "statetable" is the lexical state machine
  668.     
  669.     if (language == pascalcode)
  670.         thehandle = GetResource('TABL', 2001);
  671.     else
  672.         thehandle = GetResource('TABL', 2002);
  673.     HLock(thehandle);
  674.     statetable = *thehandle;
  675.     
  676. //    open the dictionary file...
  677.     
  678.     thefile = open(dictname, O_RDONLY);
  679.     if (thefile < 0)
  680.         {
  681.         fprintf(stderr, "Error - Unable to open "
  682.                         "dictionary file %s!\n", dictname);
  683.         return(nil);
  684.         }
  685.     
  686. //    and read it into the buffer
  687.     
  688.     thehandle = NewHandle(0);
  689.     buffersize = readinput(thefile, thehandle);
  690.     if (buffersize < 0)
  691.         {
  692.         fprintf(stderr, "Error - Unable to read %s!\n", dictname);
  693.         close(thefile);
  694.         return(nil);
  695.         }
  696.     
  697.     close(thefile);
  698.     
  699.     HLock(thehandle);
  700.     buffer = (char *)*thehandle;
  701.     
  702. //    "symbols" is the symbol table
  703.     
  704.     symbols = createnode("", "");
  705.     
  706. //    start the machine in state 0, and on line 1
  707.     
  708.     thestate = 0;
  709.     theline = 1;
  710.     errors = 0;
  711.     
  712. //    read the first identifier into “thekey”
  713.     
  714.     thestring = &thekey;
  715.     thetoken = gettoken(buffer, buffersize,
  716.                     thestring, classtable, statetable);
  717.     
  718.     while (thetoken >= 0)
  719.         {
  720.         
  721.         newstate = parsetable[3 * thestate + thetoken];
  722.         
  723.         switch (newstate)
  724.             {
  725.     
  726. //    if we got here from state 1, then we read only one
  727. //        identifier; if from state 2, we read both “thekey”
  728. //        and “thedata”
  729. //    state 0 is the beginning of a line, so increment the
  730. //        line counter and set “thestring” to “thekey”
  731.             
  732.             case 0:
  733.                 if (thestate == 1)
  734.                     thetoken = insert(symbols, thekey, thekey, 0);
  735.                 else if (thestate == 2)
  736.                     thetoken = insert(symbols, thekey, thedata, 0);
  737.                 if (thetoken == 4)
  738.                     {
  739.                     fprintf(stderr, "\n#\tDuplicate key in dictionary "
  740.                                     "file\n\tFile \"%s\"; Line %d\n",
  741.                                     dictname, theline);
  742.                     errors++;
  743.                     }
  744.                 theline++;
  745.                 thestring = &thekey;
  746.                 break;
  747.     
  748. //    having read one identifier into “thekey”, the
  749. //        next one should go into “thedata”
  750.             
  751.             case 1:
  752.                 thestring = &thedata;
  753.                 break;
  754.     
  755. //    having read one identifier into “thekey”, and the
  756. //        next one “thedata”, read anything else into
  757. //        “dummy”
  758.             
  759.             case 2:
  760.                 thestring = &dummy;
  761.                 break;
  762.     
  763. //    case 3 is the error case; if we just got here, write
  764. //        an error message
  765.             
  766.             case 3:
  767.                 if (thestate != newstate)
  768.                     fprintf(stderr, "\n#\tError in dictionary file\n"
  769.                                     "\tFile \"%s\"; Line %d\n", dictname,
  770.                                     theline);
  771.                 errors++;
  772.                 break;
  773.             
  774.             }
  775.         
  776.         thestate = newstate;
  777.         thetoken = gettoken(buffer, buffersize,
  778.                         thestring, classtable, statetable);
  779.         
  780.         }
  781.     
  782.     DisposHandle(thehandle);
  783.     
  784.     if (errors > 0)
  785.         {
  786.         destroy(symbols);
  787.         fprintf(stderr, "\n%d errors found in dictionary\n", errors);
  788.         return(nil);
  789.         }
  790.     
  791.     return(symbols);
  792.     
  793.     }
  794.     
  795. //////////////////////////////////////////////////////////////////
  796. //
  797. //        gettoken
  798. //        --------
  799. //        This routine reads the dictionary file until it finds a
  800. //        token (which it returns) or the end of the file (in
  801. //        which case it returns -1).
  802. //
  803. //////////////////////////////////////////////////////////////////
  804.     
  805. int gettoken(char *buffer, int buffersize, char *thestring,
  806.                 char *classtable, char *statetable)
  807.     {
  808.     
  809.     static int        position = 0;
  810.     
  811.     int                thestate;
  812.     unsigned char    thechar;
  813.     int                theclass;
  814.     int                newstate;
  815.     
  816. //    start the machine in state 0
  817.     
  818.     thestate = 0;
  819.     
  820.     while (position < buffersize)
  821.         {
  822.     
  823. //    read the next character, look up its class,
  824. //        and get the new state
  825.         
  826.         thechar = buffer[position++];
  827.         theclass = classtable[thechar];
  828.         newstate = statetable[classcount * thestate + theclass];
  829.         
  830.         switch (newstate)
  831.             {
  832.     
  833. //    -3 => ERR, -2 => CR; in either case, just return the
  834. //        the token number
  835.             
  836.             case -3:
  837.             case -2:
  838.                 return(- 1 - newstate);
  839.     
  840. //    -1 => ID; return the token number
  841. //        and the identifier in “thestring”
  842.             
  843.             case -1:
  844.                 *thestring = '\0';
  845.                 position--;
  846.                 return(- 1 - newstate);
  847.             
  848.             case 0:
  849.                 if (thestate == 1)
  850.                     position--;
  851.                 break;
  852.             
  853.             case 1:
  854.                 break;
  855.             
  856.             case 2:
  857.                 *thestring++ = thechar;
  858.                 break;
  859.             
  860.             }
  861.         
  862.         thestate = newstate;
  863.         
  864.         }
  865.     
  866.     return(-1);
  867.     
  868.     }
  869.     
  870. //////////////////////////////////////////////////////////////////
  871. //
  872. //        createnode
  873. //        ----------
  874. //        create and initialize a new node
  875. //
  876. //////////////////////////////////////////////////////////////////
  877.     
  878. node *createnode(char *thekey, char *thedata)
  879.     {
  880.     
  881.     node                *thenode;
  882.     int                thelength;
  883.     
  884.     thenode = (node *)NewPtr(sizeof(node));
  885.     if (thenode == nil)
  886.         return(nil);
  887.     
  888.     thelength = strlen(thekey) + 1;
  889.     thenode->key = (char *)NewPtr(thelength);
  890.     if (thenode->key == nil)
  891.         {
  892.         DisposPtr((Ptr)thenode);
  893.         return(nil);
  894.         }
  895.     BlockMove((Ptr)thekey, (Ptr)thenode->key, thelength);
  896.     
  897.     thenode->balance = 0;
  898.     thenode->left = nil;
  899.     thenode->right = nil;
  900.     
  901.     thelength = strlen(thedata) + 1;
  902.     thenode->data = (char *)NewPtr(thelength);
  903.     if (thenode->data == nil)
  904.         {
  905.         DisposPtr((Ptr)thenode->key);
  906.         DisposPtr((Ptr)thenode);
  907.         return(nil);
  908.         }
  909.     BlockMove((Ptr)thedata, (Ptr)thenode->data, thelength);
  910.     
  911.     return(thenode);
  912.     
  913.     }
  914.     
  915. //////////////////////////////////////////////////////////////////
  916. //
  917. //        insert
  918. //        ------
  919. //        Insert a new key into the table and correct balance
  920. //        factors recursively.  The routine returns a queue of
  921. //        path directions.
  922. //
  923. //////////////////////////////////////////////////////////////////
  924.     
  925. unsigned int insert(node *parent, char *thekey, char *thedata, int depth)
  926.     {
  927.     
  928.     int                difference;
  929.     node                *high;
  930.     unsigned int    result;
  931.     node                *low;
  932.     node                *child;
  933.     
  934. //    if thekey matches this node, then return an error
  935. //        (duplicate keys)
  936.     
  937.     difference = compare(thekey, parent->key);
  938.     if (difference == 0)
  939.         return(4);
  940.     
  941. //    if there's a slot for the new node, then create it and return
  942. //        1 if it is to the left of parent and 2 if to the right
  943. //    else determine high, the next step in the search path
  944.     
  945.     if (difference < 0)
  946.         {
  947.         high = parent->left;
  948.         if (high == nil)
  949.             {
  950.             parent->left = createnode(thekey, thedata);
  951.             return(1);
  952.             }
  953.         }
  954.     else
  955.         {
  956.         high = parent->right;
  957.         if (high == nil)
  958.             {
  959.             parent->right = createnode(thekey, thedata);
  960.             return(2);
  961.             }
  962.         }
  963.     
  964. //    now continue the search by calling “insert” recursively
  965. //    if the result is 0 (no balancing needed at this level) or
  966. //        the result is 4 (duplicate key), return
  967.     
  968.     result = insert(high, thekey, thedata, depth + 1);
  969.     if ((result == 0) || (result == 4))
  970.         return(result);
  971.     
  972. //    the low 4 bits of “result” indicate the direction of the
  973. //        search path leaving “high”; increment high's balance
  974. //        if the path goes left, and decrement it if the path
  975. //        goes right
  976.     
  977.     if (result % 16 == 1)
  978.         high->balance++;
  979.     else
  980.         high->balance--;
  981.     
  982. //    if the balance is now zero, no further correction of balances
  983. //        is needed, so return
  984. //    if it's ±1, push the direction away from “parent” onto the
  985. //        “result” stack and return it
  986. //    if it's ±2, continue to balancing transformation
  987.     
  988.     switch (high->balance)
  989.         {
  990.         case 0:
  991.             return(0);
  992.         case 1:
  993.         case -1:
  994.             return((result << 4) + ((difference < 0) ? 1 : 2));
  995.         case 2:
  996.         case -2:
  997.             break;
  998.         }
  999.     
  1000. //    use the direction away from “high” to find “low”, then
  1001. //        switch on that direction and the next one
  1002. //    the easiest way to follow these transformations is to
  1003. //        refer to the pictures
  1004.     
  1005.     low = (result % 16 == 1) ? high->left : high->right;
  1006.     switch (result % 256)
  1007.         {
  1008.         
  1009.         case 0x11:    //    left-left case
  1010.             
  1011.             high->left = low->right;
  1012.             low->right = high;
  1013.             if (difference < 0)
  1014.                 parent->left = low;
  1015.             else
  1016.                 parent->right = low;
  1017.             high->balance = 0;
  1018.             low->balance = 0;
  1019.             return(0);
  1020.         
  1021.         case 0x12:    //    right-left case
  1022.             
  1023.             child = low->left;
  1024.             high->right = child->left;
  1025.             low->left = child->right;
  1026.             if (difference < 0)
  1027.                 parent->left = child;
  1028.             else
  1029.                 parent->right = child;
  1030.             child->left = high;
  1031.             child->right = low;
  1032.             child->balance = 0;
  1033.             low->balance = 0;
  1034.             high->balance = 0;
  1035.             result &= 0xF00;
  1036.             if (result == 0x100)
  1037.                 low->balance = -1;
  1038.             else if (result == 0x200)
  1039.                 high->balance = 1;
  1040.             return(0);
  1041.         
  1042.         case 0x21:    //    left-right case
  1043.             
  1044.             child = low->right;
  1045.             low->right = child->left;
  1046.             high->left = child->right;
  1047.             if (difference < 0)
  1048.                 parent->left = child;
  1049.             else
  1050.                 parent->right = child;
  1051.             child->left = low;
  1052.             child->right = high;
  1053.             child->balance = 0;
  1054.             low->balance = 0;
  1055.             high->balance = 0;
  1056.             result &= 0xF00;
  1057.             if (result == 0x100)
  1058.                 high->balance = -1;
  1059.             else if (result == 0x200)
  1060.                 low->balance = 1;
  1061.             return(0);
  1062.         
  1063.         case 0x22:    //    right-right case
  1064.             
  1065.             high->right = low->left;
  1066.             low->left = high;
  1067.             if (difference < 0)
  1068.                 parent->left = low;
  1069.             else
  1070.                 parent->right = low;
  1071.             high->balance = 0;
  1072.             low->balance = 0;
  1073.             return(0);
  1074.         
  1075.         }
  1076.     
  1077.     }
  1078.     
  1079. //////////////////////////////////////////////////////////////////
  1080. //
  1081. //        lookup
  1082. //        ------
  1083. //        Find a key in the table
  1084. //
  1085. //////////////////////////////////////////////////////////////////
  1086.     
  1087. node *lookup(node *thetable, char *thekey)
  1088.     {
  1089.     
  1090.     node                *thenode;
  1091.     int                difference;
  1092.     
  1093.     thenode = thetable;
  1094.     
  1095.     while (difference = compare(thekey, thenode->key))
  1096.         {
  1097.         if (difference < 0)
  1098.             thenode = thenode->left;
  1099.         else
  1100.             thenode = thenode->right;
  1101.         if (thenode == nil)
  1102.             return(nil);
  1103.         }
  1104.     
  1105.     return(thenode);
  1106.     
  1107.     }
  1108.     
  1109. //////////////////////////////////////////////////////////////////
  1110. //
  1111. //        destroy
  1112. //        -------
  1113. //        Dispose of the table
  1114. //
  1115. //////////////////////////////////////////////////////////////////
  1116.     
  1117. void destroy(node *thetable)
  1118.     {
  1119.     
  1120.     if (thetable == nil)
  1121.         return;
  1122.     
  1123.     destroy(thetable->left);
  1124.     destroy(thetable->right);
  1125.     
  1126.     if (thetable->data)
  1127.         DisposPtr((Ptr)thetable->data);
  1128.     DisposPtr((Ptr)thetable->key);
  1129.         
  1130.     DisposPtr((Ptr)thetable);
  1131.     
  1132.     }
  1133.     
  1134. //////////////////////////////////////////////////////////////////
  1135. //
  1136. //        dump
  1137. //        -------
  1138. //        Dump the table
  1139. //
  1140. //////////////////////////////////////////////////////////////////
  1141.     
  1142. void dump(node *thetable)
  1143.     {
  1144.     
  1145.     if (thetable == nil)
  1146.         return;
  1147.     
  1148.     dump(thetable->left);
  1149.     
  1150.     printf("key = %s, data = %s, left = %s, right = %s\n",
  1151.                     thetable->key, thetable->data,
  1152.                     thetable->left ? thetable->left->key : "nil",
  1153.                     thetable->right ? thetable->right->key : "nil");
  1154.     
  1155.     dump(thetable->right);
  1156.     
  1157.     }
  1158.     
  1159. //////////////////////////////////////////////////////////////////
  1160. //
  1161. //        compare
  1162. //        -------
  1163. //        replacement for “strcmp” routine; case sensitivity is
  1164. //        determined by the global transliteration table CASETABLE.
  1165. //
  1166. //////////////////////////////////////////////////////////////////
  1167.     
  1168. int compare(unsigned char *string1, unsigned char *string2)
  1169.     {
  1170.     
  1171.     register int    char1;
  1172.     register int    char2;
  1173.     register int    difference;
  1174.     
  1175.     char1 = *string1++;
  1176.     char2 = *string2++;
  1177.     
  1178.     while (char1 || char2)
  1179.         
  1180.         {
  1181.         
  1182.         difference = CASETABLE[char1] - CASETABLE[char2];
  1183.         if (difference)
  1184.             return(difference);
  1185.         
  1186.         char1 = *string1++;
  1187.         char2 = *string2++;
  1188.         
  1189.         }
  1190.     
  1191.     return(0);
  1192.     
  1193.     }
  1194.     
  1195. //////////////////////////////////////////////////////////////////
  1196.